﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.ComponentModel;

namespace MockupDesigner.AppItems.Commands
{
    public class AlignCommand : ICommand
    {

        public CommandType CommandType
        {
            get { return CommandType.Undoable; }
        }

        private DesignerCanvas designerCanvas;
        private AlignType alignType;

        private IEnumerable<DesignerItem> selectedItems;
        private Dictionary<DesignerItem, double> elementsChanged = new Dictionary<DesignerItem, double>();

        public void Execute(object target, params object[] parameters)
        {
            if (!(target is DesignerCanvas))
            {
                throw new InvalidCastException("The target must be a DesignerCanvas");
            }
            this.designerCanvas = (DesignerCanvas)target;

            if (parameters[0] is string)
            {
                this.alignType = (AlignType)new EnumConverter(typeof(AlignType)).ConvertFrom(parameters[0]);
            }
            else if (parameters[0] is AlignType)
            {
                this.alignType = (AlignType)parameters[0];
            }
            else
            {
                throw new InvalidCastException("The target must be a AlignType");
            }

            Redo();
        }

        public void Redo()
        {
            if (this.selectedItems == null)
            {
                this.selectedItems = from item in this.designerCanvas.SelectionService.CurrentSelection.OfType<DesignerItem>()
                                where item.ParentID == Guid.Empty
                                select item;
            }

            if (selectedItems.Count() > 1)
            {
                double refValue = GetRefValue();

                foreach (DesignerItem item in selectedItems)
                {
                    double delta = GetDeltaForItem(refValue, item);
                    foreach (DesignerItem di in this.designerCanvas.SelectionService.GetGroupMembers(item))
                    {
                        ChangeValue(delta, di);
                    }
                }
            }
        }

        private void ChangeValue(double delta, DesignerItem di)
        {
            switch (this.alignType)
            {
                case AlignType.Top:
                case AlignType.Bottom:
                case AlignType.VerticalCenter:
                    double diTop = Canvas.GetTop(di);
                    if (!this.elementsChanged.ContainsKey(di))
                    {
                       this. elementsChanged.Add(di, diTop);
                    }
                    Canvas.SetTop(di, diTop + delta);
                    break;
                case AlignType.Left:
                case AlignType.Right:
                case AlignType.HorizontalCenter:
                    double diLeft = Canvas.GetLeft(di);
                    if (!this.elementsChanged.ContainsKey(di))
                    {
                        this.elementsChanged.Add(di, diLeft);
                    }
                    Canvas.SetLeft(di, diLeft + delta);
                    break;
            }
        }

        private double GetRefValue()
        {
            switch (this.alignType)
            {
                case AlignType.Top:
                    return Canvas.GetTop(this.selectedItems.First());
                case AlignType.Bottom:
                    return Canvas.GetTop(this.selectedItems.First()) + this.selectedItems.First().ActualHeight;
                case AlignType.VerticalCenter:
                    return Canvas.GetTop(this.selectedItems.First()) + this.selectedItems.First().ActualHeight / 2; ;
                case AlignType.Left:
                    return Canvas.GetLeft(this.selectedItems.First());
                case AlignType.Right:
                    return Canvas.GetLeft(this.selectedItems.First()) + this.selectedItems.First().ActualWidth;
                case AlignType.HorizontalCenter:
                    return Canvas.GetLeft(this.selectedItems.First()) + this.selectedItems.First().ActualWidth / 2;
                default:
                    return 0;
            }
        }

        private double GetDeltaForItem(double refValue, DesignerItem item)
        {
            switch (this.alignType)
            {
                case AlignType.Top:
                    return refValue - Canvas.GetTop(item);
                case AlignType.Bottom:
                    return refValue - (Canvas.GetTop(item) + item.ActualHeight); ;
                case AlignType.VerticalCenter:
                    return refValue - (Canvas.GetTop(item) + item.ActualHeight / 2);
                case AlignType.Left:
                    return refValue - Canvas.GetLeft(item);
                case AlignType.Right:
                    return refValue - (Canvas.GetLeft(item) + item.ActualWidth);
                case AlignType.HorizontalCenter:
                    return refValue - (Canvas.GetLeft(item) + item.ActualWidth / 2);
                default:
                    return 0;
            }
        }

        public void Undo()
        {
            switch (this.alignType)
            {
                case AlignType.Top:
                case AlignType.Bottom:
                case AlignType.VerticalCenter:
                    foreach (DesignerItem item in this.elementsChanged.Keys)
                    {
                        Canvas.SetTop(item, this.elementsChanged[item]);
                    }
                    break;
                case AlignType.Left:
                case AlignType.Right:
                case AlignType.HorizontalCenter:
                    foreach (DesignerItem item in this.elementsChanged.Keys)
                    {
                        Canvas.SetLeft(item, this.elementsChanged[item]);
                    }
                    break;
            }
        }

    }

    public enum AlignType
    {
        Bottom,
        Top,
        VerticalCenter,
        Left,
        Right,
        HorizontalCenter
    }
}
